// websocket协议解析 https://www.jianshu.com/p/b8d1aacd8e44
// websocket掩码处理方式 https://blog.csdn.net/yangzai187/article/details/93862980

declare type TBit = 0 | 1;

/**获取给定字节某个bit的值 */
const getBit = (byte: number, pos: number) => {
    if ((byte & (1 << pos)) == 0) return 0;
    return 1;
}

/**获取给定连续字节表示的数值 */
const getBytesValue = (data: Uint8Array, start: number, size: number) => {
    let v = 0;
    for (let i = 0; i < size; i++) {
        v += (data[start] << (8 * (size - i - 1)));
    }
    return v;
}

const opcodeDesc: { [opcode: number]: string } = {
    0: '中间数据包',
    1: 'text类型数据包',
    2: 'binary类型数据包',
    8: '断开连接类型数据包',
    9: 'ping类型数据包',
    10: 'pong类型数据包'
}

/**
 * 将wireshark的Data部分传入
 * @param data 
 */
export const decryptWS = (data: Uint8Array) => {
    const byte0 = data[0];

    const fin = getBit(byte0, 7);

    if (fin == 0) {
        // 分包，需将所有分包的数据合并
        console.log('this package is incompleted, you should keep receiving and merge all packges.');
    } else {
        // 最终包
        console.log('this package is final');
    }

    // RSV1、RSV2、RSV3：这三个值各占1位（bit），是保留字段，不使用都填充为0
    const rsv1 = getBit(byte0, 6), rsv2 = getBit(byte0, 5), rsv3 = getBit(byte0, 4);

    const opcode = byte0 & 0xf;
    console.log(opcodeDesc[opcode] || '保留数据包类型');

    const byte1 = data[1];
    let payloadLength = byte1 & 0x7f, lenthEnd = 1;
    if (payloadLength == 126) {
        // 表示第3、4字节（双字节，最大值65535）用来存储数据内容的长度
        payloadLength = getBytesValue(data, 2, 2);
        lenthEnd = 3;
    } else if (payloadLength == 127) {
        // 表示第3、4、5、6、7、8、9、10（八字节）用来存储数据内容的长度
        payloadLength = getBytesValue(data, 2, 8);
        lenthEnd = 9;
    }

    const realDatas: number[] = [];
    const mask = getBit(byte1, 7);
    if (mask == 1) {
        // payload后4个字节存储mask key
        const maskingKeySize = 4, dataPos = lenthEnd + 5;
        for (let i = dataPos, len = data.length; i < len; i++) {
            realDatas.push(data[i] ^ data[lenthEnd + 1 + (i - dataPos) % maskingKeySize]);
        }
    } else {
        // 不需要mask
        for (let i = lenthEnd + 1, len = data.length; i < len; i++) {
            realDatas.push(data[i]);
        }
    }
    return new Uint8Array(realDatas);
}
